package com.lcf.servlet;

import com.lcf.anno.ExtController;
import com.lcf.anno.ExtRequestMapping;
import com.lcf.utils.ClassUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 1.自定义DispatcherServlet<br>
 * 2.servlet init()方法初始化###只会执行一次<br>
 * ######2.1获取当前包下所有的类<br>
 * ######2.2初始化当前包下所有的类,使用Java反射机制初始化对象存放在SpringMVC容器中key(beanId)-value( 当前实例对象) <br>
 * ######2.3初始化HandlerMapping方法,将url和方法对应上 <br>
 * #######2.3.1使用Java反射技术读取类的信息,存放在map集合中key为url请求地址,value为对应方法<br>
 * ########2.3.2使用Java反射技术读取类的信息,存放在map集合中key为url请求地址,value为对应实例对象<br>
 * 3.servlet get或者post请求<br>
 * ######## 3.1.1获取请求地址,使用Java反射技术找到对应的方法和实例对象进行执行 <br>
 */
public class ExtDispatcherServlet extends HttpServlet {
    //带有ExtController注解的map
    ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<String, Object>();
    //key：请求地址       value：类
    ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();
    //key：请求地址       value：方法名称
    ConcurrentHashMap<String, String> methodBeans = new ConcurrentHashMap<String, String>();

    @Override
    public void init() throws ServletException {
        List<Class<?>> classList = ClassUtils.getClasses("com.lcf.controller");
        try {

            findClassByPackage(classList);
        } catch (Exception e) {
            System.out.println(e.getStackTrace());
        }
        extHandlerMapping();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // #################处理请求####################
        // 1.获取请求url地址
        String requestURI = req.getRequestURI();
        if (StringUtils.isEmpty(requestURI)) {
            return;
        }
        // 2.从Map集合中获取控制对象
        Object object = urlBeans.get(requestURI);
        if (object == null) {
            resp.getWriter().println(" not found 404  url");
            return;
        }
        // 3.使用url地址获取方法
        String methodName = methodBeans.get(requestURI);
        if (StringUtils.isEmpty(methodName)) {
            resp.getWriter().println(" not found method");
        }
        // 4.使用java的反射机制调用方法
        String resultPage = (String) methodInvoke(object, methodName);
        //resp.getWriter().println(resultPage);

        //5.调用视图转换器渲染给页面展示
        extResourceViewResolver(resultPage, req, resp);

    }

    private void extResourceViewResolver(String page, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String prefix = "/";
        String suffix = ".jsp";
        req.getRequestDispatcher(prefix + page + suffix).forward(req, resp);
    }

    //使用invoke执行方法
    private Object methodInvoke(Object object, String methodName) {
        try {

            Class<?> clazz = object.getClass();
            Method method = clazz.getMethod(methodName);
            Object invoke = method.invoke(object);
            return invoke;
        } catch (Exception e) {
            System.out.println(e.getStackTrace());
            return null;
        }
    }

    //找到该包下的所有类，放到map容器中
    public void findClassByPackage(List<Class<?>> classList) throws IllegalAccessException, InstantiationException {
        for (Class<?> clazz : classList) {
            //1.判断类上是否含有ExtController注解
            ExtController extController = clazz.getAnnotation(ExtController.class);
            if (null != extController) {
                //2. 获取类名作为key
                mvcBeans.put(ClassUtils.toLowerCaseFirstOne(clazz.getSimpleName()), clazz.newInstance());
            }
        }
    }

    //找到该包下的所有类，放到map容器中
    public void extHandlerMapping() {
        for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
            Object value = entry.getValue();
            String key = null;
            //1.判断类上是否含有ExtRequestMapping注解
            ExtRequestMapping extRequestMapping = value.getClass().getAnnotation(ExtRequestMapping.class);
            if (null != extRequestMapping && StringUtils.isNotBlank(extRequestMapping.value())) {
                //1.1 获取类上的路径
                key = extRequestMapping.value();
            }
            //2.获取这个类中的所有方法
            Method[] declaredMethods = value.getClass().getDeclaredMethods();
            for (Method method : declaredMethods) {
                //2.1 判断类上是否有ExtRequestMapping注解
                ExtRequestMapping methodExtRequestMapping = method.getAnnotation(ExtRequestMapping.class);
                if (null != methodExtRequestMapping) {
                    urlBeans.put(key + methodExtRequestMapping.value(), value);
                    methodBeans.put(key + methodExtRequestMapping.value(), method.getName());
                }

            }
        }
    }
}
